/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
package edu.mit.csail.relo.jdt.actions;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;

import edu.mit.csail.relo.ReloPlugin;
import edu.mit.csail.relo.actions.ListEditorInput;
import edu.mit.csail.relo.agent.BrowseModel;
import edu.mit.csail.relo.commands.AddNodeAndRelCmd;
import edu.mit.csail.relo.intro.ReloJavaConnectionUtils;
import edu.mit.csail.relo.jdt.ReloJDTPlugin;
import edu.mit.csail.relo.jdt.browse.ClassStrucBrowseModel;
import edu.mit.csail.relo.jdt.builder.ReloJDTBuilder;
import edu.mit.csail.relo.jdt.util.WaitableUIJob;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.parts.ArtifactEditPart;
import edu.mit.csail.relo.parts.ReloController;
import edu.mit.csail.relo.store.StoreUtil;
import edu.mit.csail.relo.ui.ReloEditor;
import edu.mit.csail.relo.ui.ReloView;

public class OpenForBrowsingAction implements IObjectActionDelegate {
    static final Logger logger = ReloJDTPlugin.getLogger(OpenForBrowsingAction.class);

	public void setActivePart(IAction action, IWorkbenchPart targetPart) {}
    public void setActiveEditor(IAction action, IEditorPart targetEditor) {}
    
    private ISelection sel;

    public void selectionChanged(IAction action, ISelection sel) {
        this.sel = sel;
    }

	public void run(IAction action) {
		try {
            // grab the selection first (using logger can steal the selection as a window is opened)
			final IWorkbenchWindow activeWorkbenchWindow = ReloPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();

            if (sel instanceof IStructuredSelection) {
                List selList = ((IStructuredSelection) sel).toList();
                openVizForSel(activeWorkbenchWindow, selList);
            } else {
                logger.error("Can't deal with selection type: " + sel.getClass());
                return;
            }

        } catch (Exception e) {
		    logger.error("Unexpected exception", e);
		}
	}
    
    public static class EditorAction implements IEditorActionDelegate {
        public void setActiveEditor(IAction action, IEditorPart targetEditor) {}
        public void selectionChanged(IAction action, ISelection selection) {}

        public void run(IAction action) {
            try {
                // editor actions need to request to get the selection update
                IWorkbenchWindow activeWorkbenchWindow = ReloPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
                ISelection sel = activeWorkbenchWindow.getSelectionService().getSelection();
                if (!(sel instanceof ITextSelection)) {
                	if (sel == null)
                		logger.error("text selection is null");
                	else
                		logger.error("selection of unexpetected type: " + sel.getClass() + " " + sel);
                    return;
                }
                ITextSelection txtSel = (ITextSelection)sel;
                logger.info("Received text selection: " + txtSel.getStartLine() + "," + txtSel.getEndLine());
                
                // inefficient code, but should not matter, since this does not happen often
                JavaEditor currentEditor = (JavaEditor) activeWorkbenchWindow.getActivePage().getActiveEditor();
                IJavaElement currEditorInput = SelectionConverter.getInput(currentEditor);
                IJavaElement currDeclaredElement = SelectionConverter.resolveEnclosingElement(currEditorInput, txtSel);
                IJavaElement[] prevReferencedElements = SelectionConverter.codeResolve(currEditorInput, txtSel);
    
                Set<IJavaElement> selectionSet = new HashSet<IJavaElement>();
                selectionSet.add(currEditorInput);
                selectionSet.add(currDeclaredElement);
                if (prevReferencedElements!=null) selectionSet.addAll(Arrays.asList(prevReferencedElements));
                
                List<Object> selList = Arrays.asList(selectionSet.toArray());
                openVizForSel(activeWorkbenchWindow, selList);
            } catch (Exception e) {
                logger.error("Unexpected exception", e);
            }
        }
    }
    
    static void openVizForSel(final IWorkbenchWindow activeWorkbenchWindow, final List selList) throws CoreException {
        IProject curProj = getProj(selList);
        if (curProj == null) {
            logger.error("No current project");
            return;
        }
        
        boolean connected = ReloJavaConnectionUtils.connected(curProj); 
        if (!connected) 
            ReloJavaConnectionUtils.connect(curProj);

        WaitableUIJob job = new WaitableUIJob("Waiting to Launch Relo Visualization") {
            @Override
            public IStatus runInUIThread(IProgressMonitor monitor) {
                openReloViz(activeWorkbenchWindow, selList);
                return Status.OK_STATUS;
            }
        };
        job.setUser(true);
        if (!connected) {
            // Wait for the build job to actually start, i.e. wait for 
            // 2 x Policy.MIN_BUILD_DELAY
            job.schedule(200);
            job.waitForJobWithFamily(ReloJDTBuilder.ReloFullBuildFamily);
        } else {
            job.schedule();
        }

    }
    
    static void openReloViz(IWorkbenchWindow activeWorkbenchWindow, List selList)  {
        //BrowseModel bm = new InteractionBrowseModel();
        BrowseModel bm = new ClassStrucBrowseModel();
        bm.setRepo(StoreUtil.getDefaultStoreRepository());

        // found titled editor that is visible 
		IEditorReference[] editors = activeWorkbenchWindow
				.getActivePage()
				.findEditors(null, ReloEditor.editorId, IWorkbenchPage.MATCH_ID);
		for (int i = 0; i < editors.length; i++) {
			ReloEditor editor = (ReloEditor) editors[i].getEditor(false);
//			boolean found = true;
//			ArtifactEditPart part = null;
//			for (Object item : selList) {
//				Artifact art = editor.bm.getArtifact(item);
//				if (art != null)
//					part = editor.getReloController().findArtifactEditPart(art);
//				if (art == null || part == null) {
//					found = false;
//					break;
//				}
//			}
//			if (found) {
//				activeWorkbenchWindow.getActivePage().activate(editor);
//				part.setSelected(EditPart.SELECTED_PRIMARY);
//				return;
//			}
            ReloController rc = editor.getReloController();
            CompoundCommand addCmd = new CompoundCommand();
            for (Object item : selList) {
                Artifact addArt = rc.bm.getArtifact(item);
                if (addArt == null) continue;
                logger.info("Adding: " + addArt);
                addCmd.add(new AddNodeAndRelCmd(rc, addArt));
            }
            if (addCmd.size() > 0) rc.execute(addCmd);
			activeWorkbenchWindow.getActivePage().activate(editor);
            return;
		}
        
        
        ReloView view = (ReloView) activeWorkbenchWindow.getActivePage().findView(ReloView.viewId);
        if (view != null) {
            // make sure the view is shown
            activeWorkbenchWindow.getActivePage().activate(view);
            
            ReloController rc = view.getReloController();
            CompoundCommand addCmd = new CompoundCommand();
            for (Object item : selList) {
                Artifact addArt = rc.bm.getArtifact(item);
                if (addArt == null) continue;
                logger.info("Adding: " + addArt);
                addCmd.add(new AddNodeAndRelCmd(rc, addArt));
            }
            if (addCmd.size() > 0) rc.execute(addCmd);
            //rc.execute(new AddNodeAndRelCmd(rc, extraArt))addFocusArtifacts(selList, Collections.EMPTY_MAP);
            return;
        }
            
        try {
            activeWorkbenchWindow   
                .getActivePage()
                .openEditor(new ListEditorInput(selList, bm), "edu.mit.csail.relo.editor");
        } catch (PartInitException e) {
            logger.error("Unexepected exception while opening relo visualization", e);
        }
    }

    /**
     * Gets the associated project, from any IResource's in a List 
     */
    private static IProject getProj(final List selList) {
        IProject curProj = null;
        for (Object selElement : selList) {
            if (selElement instanceof IJavaElement) {
                curProj = ((IJavaElement)selElement).getJavaProject().getProject();
            } else if (selElement instanceof IResource) {
                curProj = ((IResource)selElement).getProject();
            } else if (selElement instanceof IAdaptable) {
                IResource selRes = (IResource) ((IAdaptable)selElement).getAdapter(IResource.class);
                if (selRes != null) curProj = selRes.getProject();
            }
            if (curProj != null) break;
        }
        return curProj;
    }


}
